!*************************************************************************************!
!*                                                                                   *!
!*  MODULE:       MUtilsLib_unitTestPlatform                                         *!
!*  DEVELOPED BY: Matthew Hardy                                                      *!
!*  DESCRIPTION:  The unit test platform provides a set of moudule procedures to 
!*                support the fast and efficent creation of a unit testing framework
!*                for fortran procedures.  A variety of unit test framework can be 
!*                created depending on the users requirments                         *!
!*                                                                                   *!                                             
!*  VERSION:      1.0                                                                *!
!*                                                                                   *!
!*************************************************************************************! 
MODULE MUtilsLib_unitTestPlatform
USE kinds_dmsl_kit
use utilities_dmsl_kit,only:getspareunit
USE MUtilsLib_System
USE MUtilsLib_stringfuncs
USE MUtilsLib_MessageLog
IMPLICIT NONE
SAVE

TYPE unitTestModuleData
   INTEGER(MIK)::testIndx=undefIN,numTests=undefIN,sumryUnit=undefIN
   INTEGER(MIK),ALLOCATABLE,DIMENSION(:)::inputUnit,stndsUnit,rsltsUnit
   CHARACTER(LEN=250)::name
   CHARACTER(LEN=360)::sumryFile
   CHARACTER(LEN=360),ALLOCATABLE,DIMENSION(:)::inputFiles,stndsFiles,rsltsFiles
END TYPE unitTestModuleData

TYPE unitTestResultsData
   LOGICAL::ok=.false.
   LOGICAL::result_l=.false.
   REAL(MRK)::result_r
   INTEGER(MIK)::rslt_i=undefIN
   CHARACTER(LEN=len_stdStrL)::name=undefCH
   CHARACTER(LEN=len_stdStrL)::metaDataTag=undefCH
   CHARACTER(LEN=len_longStr)::message=undefCH
   CHARACTER(LEN=len_vShortStr)::resultString=undefCH
   
END TYPE unitTestResultsData
!
! Unit test module information and test data
TYPE(unitTestModuleData)::unitTestMod
TYPE(unitTestResultsData),ALLOCATABLE,DIMENSION(:)::unitTest

PUBLIC::unitTest_init,unitTest_fileInquire,unitTest_fileOpen,unitTest_chkResult,unitTest_writeResult,&
       fullPath,unitTest_SummarizeResults
!---
!
INTERFACE unitTest_chkResult
   MODULE PROCEDURE unitTest_intTest,unitTest_logicalTest,unitTest_realTest,unitTest_fileCompare
END INTERFACE

INTERFACE unitTest_fileOpen
   MODULE PROCEDURE unitTest_fileOpenStd,unitTest_fileOpenDirect
END INTERFACE

CONTAINS
!___________________________________________________________________________________________________________________
!
!> Unit test framework initalisation routine
!! This is the first routine called in the creation of a unit test module
SUBROUTINE unitTest_init(name,nTests,nInputFiles,nRsltsFiles,nStndsFiles,err,&
                         testNames,sumryFile,inputFiles,rsltsFiles,stndsFiles,testMetaData,&
                         open_input,open_rslts,open_stnds)
   IMPLICIT NONE    
   !
   ! Subroutine variables   
   INTEGER(MIK),INTENT(IN)::nTests                                   !> nTests         = Number of unit tests to be carried out                     
   INTEGER(MIK),INTENT(IN)::nInputFiles                              !> nInputFiles    = Number of input files used in the unit test module
   INTEGER(MIK),INTENT(IN)::nRsltsFiles                              !> nRsltsFiles    = Number of results files generated by the unit test module
   INTEGER(MIK),INTENT(IN)::nStndsFiles                              !> nStndsFiles    = Number of standards files to be compared against
   CHARACTER(LEN=*),INTENT(IN)::name                                 !> name           = Unit test module name
   CHARACTER(LEN=*),INTENT(IN),DIMENSION(:),OPTIONAL::testNames      !> testNames      = [optional] Unit test names
   CHARACTER(LEN=*),INTENT(IN),OPTIONAL::sumryFile                   !> sumryFile      = [optional] Sumamry file (full name and path - relative or absolute) - If not present the default name "Results\results_summary.txt" is assigned 
   CHARACTER(LEN=*),INTENT(IN),DIMENSION(:),OPTIONAL::testMetaData   !> testMetaData   = [optional] Unit test meta data tags
   CHARACTER(LEN=*),INTENT(IN),DIMENSION(:),OPTIONAL::inputFiles     !> inputFiles     = [optional] Array of input files (full name and path - relative or absolute)   - If not present the default name "InputFiles\unitTest_input_file_[fileNumber].txt" is assigned 
   CHARACTER(LEN=*),INTENT(IN),DIMENSION(:),OPTIONAL::rsltsFiles     !> rsltsFiles     = [optional] Array of results files (full name and path - relative or absolute) - If not present the default name "Results\unitTest_results_file_[fileNumber].txt" is assigned 
   CHARACTER(LEN=*),INTENT(IN),DIMENSION(:),OPTIONAL::stndsFiles     !> stndsFiles     = [optional] Array of standardfiles (full name and path - relative or absolute) - If not present the default name "Standards\unitTest_standard_file_[fileNumber].txt" is assigned
   LOGICAL,INTENT(IN),OPTIONAL::open_input                           !> open_input = [optional] Development agrument that prevent unit test files from being opened by the initilisation 
   LOGICAL,INTENT(IN),OPTIONAL::open_rslts                           !> open_rslts = [optional] Development agrument that prevent unit test files from being opened by the initilisation
   LOGICAL,INTENT(IN),OPTIONAL::open_stnds                           !> open_stnds = [optional] Development agrument that prevent unit test files from being opened by the initilisation
   INTEGER(MIK),INTENT(OUT)::err                                     !> err            = Error flag
   !
   ! Local variables
   INTEGER::i
   CHARACTER(LEN=360)::msg     
   LOGICAL::myResult     
   !---
   !
   ! Initialise unit test module information and index
   unitTestMod%name     = name
   unitTestMod%testIndx = 1_MIK
   unitTestMod%numTests = nTests
   IF(ALLOCATED(unitTest))DEALLOCATE(unitTest);ALLOCATE(unitTest(unitTestMod%numTests))
   !
   ! Initialise unitTest info
   unitTest%ok=.FALSE.
   DO i=1,nTests
      
      IF(PRESENT(testNames))unitTest(i)%name=testNames(i)
      IF(PRESENT(testMetaData))unitTest(i)%metaDataTag=testMetaData(i)
   END DO
   !
   ! Initalise unit test file io
   IF(ALLOCATED(unitTestMod%inputFiles))DEALLOCATE(unitTestMod%inputFiles)
   IF(ALLOCATED(unitTestMod%inputUnit))DEALLOCATE(unitTestMod%inputUnit)
   IF(nInputFiles>0)THEN
      ALLOCATE(unitTestMod%inputFiles(nInputFiles))
      ALLOCATE(unitTestMod%inputUnit(nInputFiles))
      unitTestMod%inputFiles = undefCH
      unitTestMod%inputUnit  = undefIN
            
      DO i=1,nInputFiles
         IF(PRESENT(inputFiles))THEN
            unitTestMod%inputFiles=inputFiles
         ELSE
            unitTestMod%inputFiles(i)='InputFiles\unitTest_input_file_'//i//'.txt'
         END IF
         
         CALL getspareunit(unitTestMod%inputUnit(i),err,msg)
         IF(err/=0)THEN;CALL message('E-could not assign input file unit');RETURN;END IF 
         CALL unitTest_fileOpen(fileNameAndPath=unitTestMod%inputFiles(i),unitID=unitTestMod%inputUnit(i),err=err)
         IF(err/=0)RETURN  
         
      END DO
   END IF
   
   IF(ALLOCATED(unitTestMod%rsltsFiles))DEALLOCATE(unitTestMod%rsltsFiles)
   IF(ALLOCATED(unitTestMod%rsltsUnit))DEALLOCATE(unitTestMod%rsltsUnit)
   IF(nrsltsFiles>0)THEN
      ALLOCATE(unitTestMod%rsltsFiles(nrsltsFiles))
      ALLOCATE(unitTestMod%rsltsUnit(nrsltsFiles))
      unitTestMod%rsltsFiles = undefCH
      unitTestMod%rsltsUnit  = undefIN      
      
      DO i=1,nRsltsFiles
         IF(PRESENT(rsltsFiles))THEN
            unitTestMod%rsltsFiles=rsltsFiles
         ELSE
            unitTestMod%rsltsFiles(i)='rsltsFiles\unitTest_rslts_file_'//i//'.txt'
         END IF

         CALL getspareunit(unitTestMod%rsltsUnit(i),err,msg)
         IF(err/=0)THEN;CALL message('E-could not assign input file unit');RETURN;END IF  
         CALL unitTest_fileOpen(fileNameAndPath=unitTestMod%rsltsFiles(i),unitID=unitTestMod%rsltsUnit(i),err=err)
         IF(err/=0)RETURN 

      END DO
   END IF   
   
   IF(ALLOCATED(unitTestMod%stndsFiles))DEALLOCATE(unitTestMod%stndsFiles)
   IF(ALLOCATED(unitTestMod%stndsUnit))DEALLOCATE(unitTestMod%stndsUnit)
   IF(nStndsFiles>0)THEN
      ALLOCATE(unitTestMod%stndsFiles(nstndsFiles))
      ALLOCATE(unitTestMod%stndsUnit(nstndsFiles))
      unitTestMod%stndsFiles = undefCH
      unitTestMod%stndsUnit  = undefIN  
      DO i=1,nstndsFiles
         IF(PRESENT(stndsFiles))THEN
            unitTestMod%stndsFiles=stndsFiles
         ELSE
            unitTestMod%stndsFiles(i)='stndsFiles\unitTest_stnds_file_'//i//'.txt'
         END IF
 
         CALL getspareunit(unitTestMod%stndsUnit(i),err,msg)
         IF(err/=0)THEN;CALL message('E-could not assign input file unit');RETURN;END IF 
         CALL unitTest_fileOpen(fileNameAndPath=unitTestMod%stndsFiles(i),unitID=unitTestMod%stndsUnit(i),err=err)
         IF(err/=0)RETURN                
      END DO
   END IF      
   !
   ! Open unit test module summary file and write header
   CALL getspareunit(unitTestMod%sumryUnit,err,msg)
   IF(err/=0)THEN;myResult=.FALSE.;CALL message('E-could not assign summary file unit');RETURN;END IF
   IF(PRESENT(sumryFile))THEN;unitTestMod%sumryFile=sumryFile;ELSE;unitTestMod%sumryFile="Results\results_summary.txt";END IF
   CALL unitTest_fileOpen(fileNameAndPath=unitTestMod%sumryFile,unitID=unitTestMod%sumryUnit,err=err)

   CALL unitTest_writeHeader(err=err);IF(err/=0)RETURN

   IF(.NOT.PRESENT(open_input))THEN;DO i=1,nInputFiles;CLOSE(unitTestMod%inputUnit(i));END DO;END IF
   IF(.NOT.PRESENT(open_rslts))THEN;DO i=1,nRsltsFiles;CLOSE(unitTestMod%rsltsUnit(i));END DO;END IF
   IF(.NOT.PRESENT(open_stnds))THEN;DO i=1,nStndsFiles;CLOSE(unitTestMod%stndsUnit(i));END DO;END IF

END SUBROUTINE unitTest_init
!___________________________________________________________________________________________________________________
!
!> File inquire wraper 
! 
SUBROUTINE unitTest_fileInquire(fileNameAndPath,err)
   IMPLICIT NONE
   ! Subroutine
   INTEGER,INTENT(OUT)::err                       !> Return value of file inquire, 0 = OK 
   CHARACTER(LEN=*),INTENT(IN)::fileNameAndPath   !> Full file name and path [relative or absolute]
   ! Local
   LOGICAL::fileExists  
   CHARACTER(LEN=360)::test_currentPath,test_fileNameAndPath
   !---
   !
   err=0_MIK;test_fileNameAndPath=''
   test_fileNameAndPath=fileNameAndPath
   !    
   ! CHECK FILE
   fileExists=.FALSE.
   INQUIRE(FILE=test_fileNameAndPath,EXIST=fileExists,IOSTAT=err)
   
   IF((.NOT.fileExists).OR.(err/=0))THEN
      CALL message(log_error,"E-Could not find the file "//test_fileNameAndPath(1:LEN_TRIM(test_fileNameAndPath)))
   END IF   

END SUBROUTINE unitTest_fileInquire
!___________________________________________________________________________________________________________________
!
!> File open wraper
!!
SUBROUTINE unitTest_fileOpenStd(fileNameAndPath,unitID,err,status)
   IMPLICIT NONE
   ! Subroutine
   CHARACTER(LEN=*),INTENT(IN)::fileNameAndPath   !> Full file name and path [relative or absolute]
   INTEGER(MIK),INTENT(INOUT)::unitID             !> Return unit ID
   INTEGER(MIK),INTENT(OUT)::err                  !> Return value of file inquire, 0 = OK 
   CHARACTER(LEN=*),INTENT(IN),OPTIONAL::status   !> Open status of file - Default = "UNKNOWN"
   ! 
   ! Local
   CHARACTER(LEN=7)::fileStatus
   CHARACTER(LEN=180)::msg
   !---
   !
   err=0
   
   IF(PRESENT(status))THEN;fileStatus=status;ELSE;fileStatus='UNKNOWN';END IF
   IF(Ucase(fileStatus(1:3))=="OLD")CALL unitTest_fileInquire(fileNameAndPath(1:LEN_TRIM(fileNameAndPath)),err);IF(err/=0)RETURN
   !    
   ! CHECK FILE
   OPEN(UNIT=unitID,FILE=fileNameAndPath(1:LEN_TRIM(fileNameAndPath)),IOSTAT=err,STATUS=fileStatus)
   IF(err/=0)CALL message(log_error,"Could not open the file "//fileNameAndPath(1:LEN_TRIM(fileNameAndPath)))

END SUBROUTINE 
!___________________________________________________________________________________________________________________
!
!> File open wraper
!!
SUBROUTINE unitTest_fileOpenDirect(fileType,fileID,unitID,err)
   IMPLICIT NONE
   ! Subroutine
   CHARACTER(LEN=5),INTENT(IN)::fileType          !> Unit test file type specifyer "input","rslts","stnds"
   INTEGER(MIK),INTENT(IN)::fileID                !> Unit test file specifyer
   INTEGER(MIK),INTENT(OUT)::unitID               !> Return unit ID
   INTEGER(MIK),INTENT(OUT)::err                  !> Return value of file inquire, 0 = OK 
   !---
   !
   err=0   
   
   SELECT CASE(Ucase(fileType))
       CASE("INPUT")
          UnitID=unitTestMod%inputUnit(fileID)
          OPEN(UNIT=UnitID,FILE=TRIM(unitTestMod%inputfiles(fileID)),IOSTAT=err,STATUS="UNKNOWN")       
       CASE("RSLTS")
          UnitID=unitTestMod%rsltsUnit(fileID)
          OPEN(UNIT=UnitID,FILE=TRIM(unitTestMod%rsltsfiles(fileID)),IOSTAT=err,STATUS="UNKNOWN")
       CASE("STNDS")
          UnitID=unitTestMod%stndsUnit(fileID)
          OPEN(UNIT=UnitID,FILE=TRIM(unitTestMod%stndsfiles(fileID)),IOSTAT=err,STATUS="UNKNOWN")
   END SELECT

   IF(err/=0)CALL message(log_error,"Could not open the file "//TRIM(unitTestMod%inputfiles(fileID)))

END SUBROUTINE unitTest_fileOpenDirect
!___________________________________________________________________________________________________________________
!
!> Write a file header to the output summary file
!! 
SUBROUTINE unitTest_writeHeader(err)
   IMPLICIT NONE
   ! Subroutine
   INTEGER(MIK),INTENT(OUT)::err     !> Return error
   ! Local
   INTEGER(MIK)::i,unitID
   !---
   !
   err=0
   unitID=unitTestMod%sumryUnit;REWIND(unitID)
   !
   WRITE(unitID,*,err=100)""
   WRITE(unitID,*,err=100)unitTestMod%name(1:LEN_TRIM(unitTestMod%name))
   WRITE(unitID,*,err=100)""
   
   IF(SIZE(unitTestMod%inputFiles)>0)THEN
      DO i=1,SIZE(unitTestMod%inputFiles)
         SELECT CASE(i)
            CASE(1); WRITE(unitID,*,err=100)"Input files:          "//unitTestMod%inputFiles(i)(1:LEN_TRIM(unitTestMod%inputFiles(i)))
            CASE(2:);WRITE(unitID,*,err=100)"                      "//unitTestMod%inputFiles(i)(1:LEN_TRIM(unitTestMod%inputFiles(i)))
         END SELECT   
      END DO
   END IF
   
   IF(SIZE(unitTestMod%stndsFiles)>0)THEN
      WRITE(unitID,*,err=100)""
      DO i=1,SIZE(unitTestMod%stndsFiles)
         SELECT CASE(i)
            CASE(1); WRITE(unitID,*,err=100)"Standard files:       "//unitTestMod%stndsFiles(i)(1:LEN_TRIM(unitTestMod%stndsFiles(i)))
            CASE(2:);WRITE(unitID,*,err=100)"                      "//unitTestMod%stndsFiles(i)(1:LEN_TRIM(unitTestMod%stndsFiles(i)))
         END SELECT
      END DO
   END IF

   IF(SIZE(unitTestMod%rsltsFiles)>0)THEN
      WRITE(unitID,*,err=100)""
      DO i=1,SIZE(unitTestMod%rsltsFiles)
         SELECT CASE(i)
            CASE(1); WRITE(unitID,*,err=100)"Results files created: "//unitTestMod%rsltsFiles(i)(1:LEN_TRIM(unitTestMod%rsltsFiles(i)))
            CASE(2:);WRITE(unitID,*,err=100)"                       "//unitTestMod%rsltsFiles(i)(1:LEN_TRIM(unitTestMod%rsltsFiles(i)))
         END SELECT      
      END DO
   END IF
   
   WRITE(unitID,*,err=100)""
   WRITE(unitID,*,err=100)"Unit Test perfomed at: "//systemDateTime()
   WRITE(unitID,*,err=100)""

   RETURN
   
100 CONTINUE
   err=-1
   CALL message(log_error,"Error writing header for results summary file")
 
END SUBROUTINE unitTest_writeHeader
!___________________________________________________________________________________________________________________
!
!> Write a message to a file 
!! the default file is the results summary file 
SUBROUTINE unitTest_writeMsg(myMessage,unitID,addLine_before,addLine_after)
   IMPLICIT NONE
   ! Subroutine variables
   CHARACTER(LEN=*),INTENT(IN)::myMessage
   INTEGER(MIK),INTENT(IN),OPTIONAL::unitID,addLine_before,addLine_after
   !
   ! Local varaibles
   INTEGER(MIK)::i,localUnitID
   !---
   !
   IF(PRESENT(unitID))THEN;localUnitID=unitID;ELSE;localUnitID=unitTestMod%sumryUnit;END IF
   IF(PRESENT(addLine_before))THEN;DO i=1,addLine_before;WRITE(localUnitID,*,err=101)"";END DO;END IF
   WRITE(localUnitID,"(a)",err=101)myMessage(1:LEN_TRIM(myMessage))
   IF(PRESENT(addLine_after))THEN;DO i=1,addLine_after;WRITE(localUnitID,*,err=101)"";END DO;END IF
   RETURN
   
101 CONTINUE
   CALL message(log_error,"Error writing message to unit test module summary file")
 
END SUBROUTINE unitTest_writeMsg
!__________________________________________________________________________________________________________________
!> Compares files and evalutes whether they are the same
!! Comparison can be undertaken at various levels of detail, see compareLevel arg
SUBROUTINE unitTest_fileCompare(fileOne,fileTwo,CompareLevel,skip,outputUnit,testName,testResult,message)
   use mUtilsLib_System, only : fileCompare_External
   use mUtilsLib_fileio, only : fileExist,findEOF
   use mUtilsLib_StringFuncs, only: operator(//)
   use MUtilsLib_VarFuncs, only : CheckPresent
   use mutilslib_messagelog, only : log_message=>message
   
   ! Compare two files
   IMPLICIT NONE
   ! Subroutine variables
   character(len=*),intent(in) :: fileOne, fileTwo              !> Full path names for files to be compared
   integer(mik), intent(in),optional :: CompareLevel !> Provides different levels of comparison
                                                     !! 1 = LineCount
                                                     !! 2 = 1 + Line by Line Comparison
                                                     !! 3 = 2 + External File Compare(e.g. using tortoisemerge)
   INTEGER(MIK),INTENT(IN),OPTIONAL:: skip           !> Number of lines to skip if doing line by line comparison
   INTEGER(MIK),INTENT(IN),OPTIONAL::outputUnit      !> optional output unit number - Default id the summary file unit
   CHARACTER(LEN=*),INTENT(IN),OPTIONAL::testName    !> optional assinging of unit test name
   LOGICAL,INTENT(OUT),OPTIONAL::testResult          !> optional return of test value
   CHARACTER(LEN=*),INTENT(IN),OPTIONAL::message

   ! General variables
   INTEGER(MIK)::i,indx,err,fileOneLineCount,fileTwoLineCount,endOfFile_i,endOfFile_ii,unitOneLc,unitTwoLc,ok
   CHARACTER(LEN=1)::dummy 
   CHARACTER(LEN=len_vLongStr)::textone,texttwo
   CHARACTER(LEN=len_vLongStr)::msg
   INTEGER(MIK)::CompareLevelLc
   LOGICAL::myTestResult
   !---
   !
   indx=unitTestMod%testIndx
   if (indx>SIZE(unitTest)) then ; call log_message("No. of unit tests has been exceeded");  return ;  end if
   
   IF(PRESENT(testName))unitTest(indx)%name=testName     
   IF(PRESENT(message))unitTest(indx)%message=message
        
   CompareLevelLc=checkPresent(Comparelevel,2) 
   unitOneLc=9991  ! Need to add function to get New Unit, TODO
   unitTwoLc=9992  ! Need to add function to get New Unit, TODO
   
  
   ! Check if files exist
   if(.not.fileExist(fileToOpen=fileOne,msg=msg)) then; call log_message(trim(msg)//" in F: unitTest_fileCompare"); myTestResult=.false.; return; end if
   if(.not.fileExist(fileToOpen=fileTwo,msg=msg)) then; call log_message(trim(msg)//" in F: unitTest_fileCompare"); myTestResult=.false.; return; end if
   
   ! Undertake line count
   fileOneLineCount=findEof(filepath=fileOne,err=ok,msg=msg)
   if (ok/=0) then; call log_message(trim(msg)//" in F: unitTest_fileCompare"); myTestResult=.false.; return; end if
   fileTwoLineCount=findEof(filepath=fileTwo,err=ok,msg=msg)
   if (ok/=0) then; call log_message(trim(msg)//" in F: unitTest_fileCompare"); myTestResult=.false.; return; end if
        
   ! Open files to unitOne and UnitTwo
   call unitTest_fileOpen(fileNameAndPath=fileOne,unitID=unitOneLc,err=err,status="OLD")
   if(err/=0) then
      myTestResult=.FALSE.
      call log_message("Unable to open file:"//trim(fileOne)//" in F: unitTest_fileCompare"); return
   end if
   call unitTest_fileOpen(fileNameAndPath=fileTwo,unitID=unitTwoLc,err=err,status="OLD")
   if(err/=0) then
      myTestResult=.FALSE.
      call log_message("Unable to open file:"//trim(fileTwo)//" in F: unitTest_fileCompare"); return
   end if
       
    
  ! Compare file line numbers
  if (ComparelevelLc>0) then
   REWIND(UNIT=unitOneLc);REWIND(UNIT=unitTwolc)
   IF((fileOneLineCount-fileTwoLineCount)/=0)THEN;
    myTestResult=.FALSE.
    CLOSE(UNIT=unitOneLc);CLOSE(UNIT=unitTwolc)
    IF(compareLevelLc>2) call call_FileCompare_External()
    CALL manageResult()
    RETURN
   ELSE; 
    myTestResult=.TRUE.
    if(ComparelevelLc==1) CALL manageResult()
   END IF
  END IF 

  
  ! Compare line-by-line
  if (CompareLevelLc>1) then 
   REWIND(UNIT=unitOneLc);REWIND(UNIT=unitTwolc)
   IF(PRESENT(skip))THEN
      DO i=1,skip;READ(unitOneLc,"(a)",IOSTAT=endOfFile_i) textone;READ(unitTwolc,"(a)",IOSTAT=endOfFile_ii)texttwo;END DO
   END IF
   
   DO
      textone='';texttwo=''
      READ(unitOneLc,"(a)",IOSTAT=endOfFile_i)textone;READ(unitTwolc,"(a)",IOSTAT=endOfFile_ii)texttwo
      IF((endOfFile_i==(-1)).and.(endOfFile_ii==(-1)))THEN;
       myTestResult=.TRUE.;CALL manageResult();EXIT;
      ELSE IF ((endOfFile_i==0) .and. (endOfFile_ii==0)) then
       IF(textone(1:LEN_TRIM(texttwo))/=texttwo(1:LEN_TRIM(texttwo)))THEN
        myTestResult=.FALSE.
        CLOSE(UNIT=unitOneLc);CLOSE(UNIT=unitTwolc)
        if(compareLevelLc>2) call call_FileCompare_External()
        CALL manageResult()
        RETURN
       END IF
      END IF
   END DO
  END IF
  
  CLOSE(UNIT=unitOneLc);CLOSE(UNIT=unitTwoLc)
  RETURN  
CONTAINS 

SUBROUTINE Call_FileCompare_External()
    
    CLOSE(unitOneLc,iostat=ok)
    CLOSE(unitTwoLc,iostat=ok)
    ok=FileCompare_External(fileOne=fileOne,fileTwo=FileTwo)
    IF (ok/=0) CALL log_message("Unable to compare files using external viewer:"//trim(fileone)//" and"//trim(filetwo)//" in f:unitTest_fileCompare")
    RETURN

END SUBROUTINE Call_FileCompare_External
   
SUBROUTINE manageResult()
  !
  ! Output Result
  unitTest(indx)%ok=myTestResult
   
  IF(PRESENT(outputUnit))THEN
     CALL unitTest_writeResult(testName=unitTest(indx)%name,testResult=unitTest(indx)%ok,failMessage=unitTest(indx)%message,unitID=outputUnit,err=err)
  ELSE
     CALL unitTest_writeResult(testName=unitTest(indx)%name,testResult=unitTest(indx)%ok,failMessage=unitTest(indx)%message,unitID=unitTestMod%sumryUnit,err=err)
  END IF
  !
  ! Increment internal test index
  unitTestMod%testIndx=unitTestMod%testIndx+1   

END SUBROUTINE manageResult
   
END SUBROUTINE unitTest_fileCompare
!__________________________________________________________________________________________________________________
!
!> Test if real variable is within a specified tollerence of a known value
!! Accessed via the common interface testMyResul(...,...,)
SUBROUTINE unitTest_realTest(testVal,val_true,tol,err,message,outputUnit,testName)
   use mutilslib_messagelog, only : log_message=>message
   IMPLICIT NONE
   ! Subroutine
   REAL(MRK),INTENT(IN)::testVal,val_true,tol
   INTEGER(MIK),INTENT(OUT)::err
   INTEGER(MIK),INTENT(IN),OPTIONAL::outputUnit
   CHARACTER(LEN=*),INTENT(IN),OPTIONAL::testName,message
   !
   ! Local variables
   INTEGER(MIK)::indx
   !---
   !
   indx=unitTestMod%testIndx
   if (indx>SIZE(unitTest)) then ; call log_message("No. of unit tests has been exceeded");  return ;  end if
   unitTest(indx)%rslt_i=testVal
   IF(PRESENT(testName))unitTest(indx)%name=testName
   IF(PRESENT(message))unitTest(indx)%message=message
      
   IF(ABS(testVal)-ABS(val_true)<=tol)THEN
      unitTest(indx)%ok=.TRUE.
   ELSE
      unitTest(indx)%ok=.FALSE.
   END IF

   IF(PRESENT(outputUnit))THEN
      CALL unitTest_writeResult(testName=unitTest(indx)%name,testResult=unitTest(indx)%ok,failMessage=unitTest(indx)%message,unitID=outputUnit,err=err)
   ELSE
      CALL unitTest_writeResult(testName=unitTest(indx)%name,testResult=unitTest(indx)%ok,failMessage=unitTest(indx)%message,unitID=unitTestMod%sumryUnit,err=err)
   END IF
   !
   ! Increment internal test index
   unitTestMod%testIndx=unitTestMod%testIndx+1
   
END SUBROUTINE unitTest_realTest
!__________________________________________________________________________________________________________________
!
SUBROUTINE unitTest_intTest(testVal,err,val_true,val_false,message,outputUnit,testName)
   use mutilslib_messagelog, only : log_message=>message
   IMPLICIT NONE
   ! Subroutine
   INTEGER(MIK),INTENT(IN)::testVal
   INTEGER(MIK),INTENT(OUT)::err
   INTEGER(MIK),INTENT(IN),OPTIONAL::val_true,val_false,outputUnit
   CHARACTER(LEN=*),INTENT(IN),OPTIONAL::testName,message
   !
   ! Local variables
   INTEGER(MIK)::indx
   !---
   !
   indx=unitTestMod%testIndx
   if (indx>SIZE(unitTest)) then ; call log_message("No. of unit tests has been exceeded");  return ;  end if
   
   unitTest(indx)%rslt_i=testVal
   IF(PRESENT(testName))unitTest(indx)%name=testName
   IF(PRESENT(message))unitTest(indx)%message=message

   IF(PRESENT(val_true))THEN
     IF(testVal==val_true)THEN;unitTest(indx)%ok=.TRUE.;ELSE;unitTest(indx)%ok=.FALSE.;END IF
   ELSE IF(PRESENT(val_false))THEN 
     IF(testVal==val_false)THEN;unitTest(indx)%ok=.FALSE.;ELSE;unitTest(indx)%ok=.TRUE.;END IF   
   END IF

   IF(PRESENT(outputUnit))THEN
      CALL unitTest_writeResult(testName=unitTest(indx)%name,testResult=unitTest(indx)%ok,failMessage=unitTest(indx)%message,unitID=outputUnit,err=err)
   ELSE
      CALL unitTest_writeResult(testName=unitTest(indx)%name,testResult=unitTest(indx)%ok,failMessage=unitTest(indx)%message,unitID=unitTestMod%sumryUnit,err=err)
   END IF
   !
   ! Increment internal test index
   unitTestMod%testIndx=unitTestMod%testIndx+1
   continue
   
END SUBROUTINE unitTest_intTest
!__________________________________________________________________________________________________________________
!
SUBROUTINE unitTest_logicalTest(testVal,err,val_true,val_false,message,outputUnit,testName)
   use mutilslib_messagelog, only : log_message=>message
   IMPLICIT NONE
   LOGICAL,INTENT(IN)::testVal
   LOGICAL,INTENT(IN),OPTIONAL::val_true,val_false
   INTEGER(MIK),INTENT(OUT)::err
   INTEGER(MIK),INTENT(IN),OPTIONAL::outputUnit
   CHARACTER(LEN=*),INTENT(IN),OPTIONAL::testName,message
   !
   ! Local variables
   INTEGER(MIK)::indx
   !---
   indx=unitTestMod%testIndx
   if (indx>SIZE(unitTest)) then ; call log_message("No. of unit tests has been exceeded");  err=-1; return ;  end if
   
   unitTest(indx)%result_l=testVal
   IF(PRESENT(testName))unitTest(indx)%name=testName
   IF(PRESENT(message))unitTest(indx)%message=message

   IF(PRESENT(val_true))THEN
     IF(testVal==val_true)THEN;unitTest(indx)%ok=.TRUE.;ELSE;unitTest(indx)%ok=.FALSE.;END IF
   ELSE IF(PRESENT(val_false))THEN 
     IF(testVal==val_false)THEN;unitTest(indx)%ok=.FALSE.;ELSE;unitTest(indx)%ok=.TRUE.;END IF   
   ELSE IF(testVal)THEN 
     unitTest(indx)%ok=.TRUE.   
   ELSE 
     unitTest(indx)%ok=.FALSE.
   END IF

   IF(PRESENT(outputUnit))THEN
      CALL unitTest_writeResult(testName=unitTest(indx)%name,testResult=unitTest(indx)%ok,failMessage=unitTest(indx)%message,unitID=outputUnit,err=err)
   ELSE
      CALL unitTest_writeResult(testName=unitTest(indx)%name,testResult=unitTest(indx)%ok,failMessage=unitTest(indx)%message,unitID=unitTestMod%sumryUnit,err=err)
   END IF
   !
   ! Increment internal test index
   unitTestMod%testIndx=unitTestMod%testIndx+1
      
END SUBROUTINE unitTest_logicalTest
!__________________________________________________________________________________________________________________
!
SUBROUTINE unitTest_writeResult(testName,testResult,failMessage,unitID,err)
   IMPLICIT NONE
   ! Subroutine
   INTEGER(MIK),INTENT(IN)::unitID
   LOGICAL,INTENT(IN)::testResult
   CHARACTER(LEN=*),INTENT(IN)::testName
   CHARACTER(LEN=*),INTENT(IN)::failMessage
   INTEGER(MIK),INTENT(OUT)::err
   !
   ! Local
   CHARACTER(LEN=len_vLongStr)::outputString   
   !---
   !
   outputString=""

   SELECT CASE(testResult)
       CASE(.TRUE.) ; WRITE(outputString,*,err=102)testName(1:LEN_TRIM(testName))
                      WRITE(outputString((LEN(testName)+2):LEN(testName)+6),"(a)",err=102)"PASS"
                      WRITE(unitID,'(a)',err=102)outputString(1:LEN_TRIM(outputString))
       
       CASE(.FALSE.); WRITE(outputString,*,err=102)testName(1:LEN_TRIM(testName))
                      WRITE(outputString((LEN(testName)+2):LEN(testName)+6),"(a)",err=102)"FAIL"
                      WRITE(unitID,'(a)',err=102)outputString(1:LEN_TRIM(outputString))
                      WRITE(unitID,'(a)',err=102)"                     "//failMessage(1:LEN_TRIM(failMessage))    
   END SELECT

   RETURN
   
102 CONTINUE
   err=-1
   CALL message(log_error,"Error writing test results for "//testName(1:LEN_TRIM(testName)))
 
 END SUBROUTINE unitTest_writeResult
!__________________________________________________________________________________________________________________
 
 SUBROUTINE unitTest_SummarizeResults(allTestsPass,failmessage,viewSummaryFile,viewFile_ifNotAllTestPass)
   ! Summarize all unit test results and add to file
   use mutilslib_stringfuncs, only : operator(//)
   IMPLICIT NONE
   ! Subroutine
   logical(mlk), intent(out),optional ::  allTestsPass 
   logical(mlk), intent(in),optional ::  viewSummaryFile
   character(len=*), intent(in),optional ::  failMessage
   character(len=*), intent(in),optional ::  viewFile_ifNotAllTestPass
   
   ! Local
   CHARACTER(LEN=360)::outputString   
   INTEGER(MIK)::ntests,nTestsFail,i
   !---
   !
   outputString=""
   WRITE(unitTestMod%sumryUnit,*)
   
   nTests=SIZE(unitTest)
   nTestsFail=0
   
   do i=1,ntests
    if(.not.unitTest(i)%ok) nTestsFail=nTestsFail+1
   end do
      
   if (nTestsFail==0) then
    if(present(allTestsPass)) allTestsPass =.true.   
    outputString="All Tests passed"
    WRITE(unitTestMod%sumryUnit,'(a)') "Summary of Test Results: "//trim(outputString)
   else 
    if(present(allTestsPass)) allTestsPass =.false.   
    outputString=nTestsFail//" of "//nTests//" Tests failed"
    WRITE(unitTestMod%sumryUnit,'(a)') "Summary of Test Results: "//trim(outputString)
    WRITE(unitTestMod%sumryUnit,*) 
    if (present(failmessage)) WRITE(unitTestMod%sumryUnit,"(a)") failMessage
    
   end if
   
   ! Option to view Summary Results File
   if (present(viewSummaryFile)) then 
    if (viewSummaryFile) call viewtxtfile(unitTestMod%sumryFile)
   end if
   
   ! Option to view a specific user specified file, if tests do not pass
   if (present(viewFile_ifNotAllTestPass)) then 
    if (.not.nTestsFail==0) call viewtxtfile(viewFile_ifNotAllTestPass)
   end if
   
   
 END SUBROUTINE unitTest_SummarizeResults
!___________________________________________________________________________________________________________________
!
SUBROUTINE unitTest_initGlobalErrLog(action)
   IMPLICIT NONE
   ! Subroutine
   CHARACTER(LEN=*),INTENT(IN)::action
   ! Local
   LOGICAL::fileOpen
   !---
   !
   SELECT CASE(action(1:LEN_TRIM(action)))
      CASE("open","OPEN")
         CALL init_log() ! Ensures that all log messages are appended to the same file
      CASE("close","CLOSE")
         CALL close_log()
   END SELECT
 
END SUBROUTINE unitTest_initGlobalErrLog
!___________________________________________________________________________________________________________________
!
!> Creates a full file name and path string [relative  or absolute] from and input file name
!! and or path.
FUNCTION fullPath(fileName,filePath) RESULT(fullPathStr)
!
use MUtilsLib_System,ONLY:findcurrentdir
use MUtilsLib_StringFuncs
use kinds_dmsl_kit, only: len_vLongStr
IMPLICIT NONE
CHARACTER(LEN=*),INTENT(IN),OPTIONAL::fileName   !> File name including extension eg testData.txt
CHARACTER(LEN=*),INTENT(IN),OPTIONAL::filePath   !> File path [relative or absolute] eg C:\devel\
CHARACTER(LEN=len_vLongStr)::fullPathStr        !> File name and path [relative or absolute] eg C:\devel\testData.txt
!
! Local variables
CHARACTER(LEN=360)::currentDir,name,path
 !---
 !
 ! DEFINE FULL NAME AND PATH STRING
 IF((PRESENT(fileName)).AND.(.NOT.PRESENT(filePath)))THEN
    name=remove_startslash(fileName)
    currentDir=findcurrentdir()
    fullPathStr=currentDir(1:LEN_TRIM(currentDir))//name(1:LEN_TRIM(name))
    
 ELSE IF((PRESENT(fileName)).AND.(PRESENT(filePath)))THEN
    name=remove_startslash(fileName)
    path=add_endslash(filePath)
    fullPathStr=path(1:LEN_TRIM(path))//name(1:LEN_TRIM(name))
 END IF

END FUNCTION

END MODULE MUtilsLib_unitTestPlatform

